package icerts

import (
	"encoding/json"
	"fmt"
	"sync"

	"gitee.com/djaf/my-certs/app/classes/certificates"
	"gitee.com/djaf/my-certs/app/data/dxo"
	"gitee.com/djaf/my-certs/app/web/dto"
	"github.com/starter-go/application"
	"github.com/starter-go/base/lang"
	"github.com/starter-go/security/random"
	"github.com/starter-go/vlog"
	"gorm.io/gorm"
)

// CsrDaoImpl ...
type CsrDaoImpl struct {

	//starter:component

	_as func(certificates.CSRDAO) //starter:as("#")

	UUIDService random.UUIDService //starter:inject("#")

	idGen dxo.CSRID
	table map[dxo.CSRID]*csrEntityHolder
	mutex sync.Mutex
}

func (inst *CsrDaoImpl) _impl() certificates.CSRDAO {
	return inst
}

// Life ...
func (inst *CsrDaoImpl) Life() *application.Life {
	return &application.Life{
		OnCreate: inst.init,
	}
}

func (inst *CsrDaoImpl) init() error {
	now := lang.Now()
	inst.idGen = dxo.CSRID(now)
	inst.table = make(map[dxo.CSRID]*csrEntityHolder)
	return nil
}

func (inst *CsrDaoImpl) getTable() map[dxo.CSRID]*csrEntityHolder {
	t := inst.table
	if t == nil {
		t = make(map[dxo.CSRID]*csrEntityHolder)
		inst.table = t
	}
	return t
}

func (inst *CsrDaoImpl) nextID() dxo.CSRID {
	inst.idGen++
	return inst.idGen
}

// Insert ...
func (inst *CsrDaoImpl) Insert(db *gorm.DB, item *dto.CSR) (*dto.CSR, error) {

	inst.mutex.Lock()
	defer func() {
		inst.mutex.Unlock()
	}()

	id := inst.nextID()
	uuid := inst.UUIDService.Build().Class("entity.Certificate").Generate()
	table := inst.getTable()

	h := table[id]
	if h != nil {
		return nil, fmt.Errorf("the id of CSR is dup:%s", id)
	}

	item.ID = id
	item.UUID = uuid

	h = new(csrEntityHolder)
	h.id = id
	h.uuid = uuid
	h.set(item)

	table[id] = h
	return item, nil
}

// Update ...
func (inst *CsrDaoImpl) Update(db *gorm.DB, id dxo.CSRID, fn func(item *dto.CSR)) (*dto.CSR, error) {

	inst.mutex.Lock()
	defer func() {
		inst.mutex.Unlock()
	}()

	table := inst.getTable()
	h := table[id]
	if h == nil {
		return nil, fmt.Errorf("no CSR with id:%s", id)
	}

	item := h.get()
	fn(item)
	h.set(item)
	return item, nil
}

// Remove ...
func (inst *CsrDaoImpl) Remove(db *gorm.DB, id dxo.CSRID) error {

	inst.mutex.Lock()
	defer func() {
		inst.mutex.Unlock()
	}()

	table := inst.getTable()
	h := table[id]
	if h == nil {
		return fmt.Errorf("no CSR with id:%s", id)
	}

	table[id] = nil
	return nil
}

// Find ...
func (inst *CsrDaoImpl) Find(db *gorm.DB, id dxo.CSRID) (*dto.CSR, error) {

	inst.mutex.Lock()
	defer func() {
		inst.mutex.Unlock()
	}()

	table := inst.getTable()
	h := table[id]
	if h == nil {
		return nil, fmt.Errorf("no CSR with id:%s", id)
	}

	return h.get(), nil
}

////////////////////////////////////////////////////////////////////////////////

type csrEntityHolder struct {
	id   dxo.CSRID
	uuid lang.UUID
	json string
}

func (inst *csrEntityHolder) get() *dto.CSR {
	dst := new(dto.CSR)
	str := inst.json
	err := json.Unmarshal([]byte(str), dst)
	if err != nil {
		vlog.Warn(err.Error())
	}
	return dst
}

func (inst *csrEntityHolder) set(obj *dto.CSR) {
	bin, err := json.Marshal(obj)
	if err != nil {
		vlog.Warn(err.Error())
	}
	inst.json = string(bin)
}
